/**
 * websocket 数据打包 / 解包工具
 * 数据结构: type(1) + uuid(16) + time(4) + index(2) + count(2) + peerLength(2) + peer + payload
 */
export interface FileInfo {
    name: string;
    size: number;
    mime: string;
}

export interface PeerInfo {
    notice_type?: number | null;
    sender?: string | null;
    receiver?: string | string[] | null;
    group_code?: string | null;
    group?: string | null;
    files?: FileInfo[] | null;
}

export interface UnpackedMessage {
    type: number;
    uuid: string;
    datetime: number;
    index: number;
    count: number;
    notice_type: number | null;
    sender: string | number | null;
    receiver: string | string[] | number | null;
    group: string | null;
    files: FileInfo[];
    payload: string | Blob | Uint8Array;
}

export type MessageCallback = (msg: UnpackedMessage) => void;

export function pack(
    type: number,
    uuid: string,
    datetime: number,
    index: number,
    count: number,
    peer: PeerInfo,
    payload: string | Uint8Array | ArrayBuffer | object
): ArrayBuffer {
    const encoder = new TextEncoder();
    const peerJson = JSON.stringify(peer);
    const peerBytes = encoder.encode(peerJson);
    const peerLength = peerBytes.length;
    // UUID 转16字节
    const uuidHex = uuid.replace(/-/g, '');
    const uuidBin = new Uint8Array(16);
    for (let i = 0; i < 16; i++) {
        uuidBin[i] = parseInt(uuidHex.slice(i * 2, i * 2 + 2), 16);
    }
    // payload 转字节
    let payloadBytes: Uint8Array;
    if (payload instanceof ArrayBuffer) payloadBytes = new Uint8Array(payload);
    else if (payload instanceof Uint8Array) payloadBytes = payload;
    else if (typeof payload === 'string') payloadBytes = encoder.encode(payload);
    else payloadBytes = encoder.encode(JSON.stringify(payload));
    // 总长度计算
    const totalLength = 1 + 16 + 4 + 2 + 2 + 2 + peerLength + payloadBytes.length;
    const buffer = new ArrayBuffer(totalLength);
    const view = new DataView(buffer);
    let offset = 0;

    view.setUint8(offset, type);
    offset += 1;
    new Uint8Array(buffer, offset, 16).set(uuidBin);
    offset += 16;
    view.setUint32(offset, datetime, false);
    offset += 4; // 大端
    view.setUint16(offset, index, false);
    offset += 2;
    view.setUint16(offset, count, false);
    offset += 2;
    view.setUint16(offset, peerLength, false);
    offset += 2;

    new Uint8Array(buffer, offset, peerLength).set(peerBytes);
    offset += peerLength;
    new Uint8Array(buffer, offset, payloadBytes.length).set(payloadBytes);
    return buffer;
}

export function unpack(arrayBuffer: ArrayBuffer): UnpackedMessage {
    const dv = new DataView(arrayBuffer);
    const decoder = new TextDecoder();
    let offset = 0;
    const type = dv.getUint8(offset);
    offset += 1;
    // UUID
    const uuidBytes = new Uint8Array(arrayBuffer, offset, 16);
    offset += 16;
    const uuidHex = Array.from(uuidBytes).map(b => b.toString(16).padStart(2, '0')).join('');
    const uuid = `${uuidHex.slice(0, 8)}-${uuidHex.slice(8, 12)}-${uuidHex.slice(12, 16)}-${uuidHex.slice(16, 20)}-${uuidHex.slice(20)}`;

    const datetime = dv.getUint32(offset, false);
    offset += 4;
    const index = dv.getUint16(offset, false);
    offset += 2;
    const count = dv.getUint16(offset, false);
    offset += 2;
    let peerLength = dv.getUint16(offset, false);
    offset += 2;

    // 防止越界
    const remaining = dv.byteLength - offset;
    if (peerLength > remaining) {
        console.warn(`⚠️ peerLength(${peerLength}) 超出剩余长度(${remaining})，自动修正`);
        peerLength = remaining;
    }
    // 解析 peer JSON
    const peerBytes = new Uint8Array(arrayBuffer, offset, peerLength);
    offset += peerLength;
    let peer: PeerInfo = {};
    try {
        peer = JSON.parse(decoder.decode(peerBytes));
    } catch (e) {
        console.error('peer JSON 解析失败', e);
    }
    const payloadBytes = new Uint8Array(arrayBuffer, offset);
    return {
        type,
        uuid,
        datetime,
        index,
        count,
        notice_type: peer.notice_type ?? null,
        sender: peer.sender ?? null,
        receiver: peer.receiver ?? null,
        group: peer.group_code ?? peer.group ?? null,
        files: peer.files ?? [],
        payload: payloadBytes
    };
}
